﻿#include "StdAfx.h"
#include <math.h>

namespace DuiLib {
#define HSLMAX   255    /* H,L, and S vary over 0-HSLMAX */
#define RGBMAX   255    /* R,G, and B vary over 0-RGBMAX */
#define HSLUNDEFINED (HSLMAX*2/3)

    /*
    * Convert hue value to RGB
    */
    static float HueToRGB(float v1, float v2, float vH)
    {
        if (vH < 0.0f) vH += 1.0f;
        if (vH > 1.0f) vH -= 1.0f;
        if ((6.0f * vH) < 1.0f) return (v1 + (v2 - v1) * 6.0f * vH);
        if ((2.0f * vH) < 1.0f) return (v2);
        if ((3.0f * vH) < 2.0f) return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f);
        return (v1);
    }

    /*
    * Convert color RGB to HSL
    * pHue HSL hue value            [0 - 1]
    * pSat HSL saturation value        [0 - 1]
    * pLue HSL luminance value        [0 - 1]
    */

    static void RGBToHSL(DWORD clr, float *pHue, float *pSat, float *pLue)
    {
        float R = (float)(GetRValue(clr) / 255.0f);  //RGB from 0 to 255
        float G = (float)(GetGValue(clr) / 255.0f);
        float B = (float)(GetBValue(clr) / 255.0f);

        float H, S, L;

        float fMin = min(R, min(G, B));        //Min. value of RGB
        float fMax = max(R, max(G, B));        //Max. value of RGB
        float fDelta = fMax - fMin;                //Delta RGB value

        L = (fMax + fMin) / 2.0f;

        if (fDelta == 0)                     //This is a gray, no chroma...
        {
            H = 0.0f;                          //HSL results from 0 to 1
            S = 0.0f;
        }
        else                                   //Chromatic data...
        {
            float del_R, del_G, del_B;

            if (L < 0.5) S = fDelta / (fMax + fMin);
            else           S = fDelta / (2.0f - fMax - fMin);

            del_R = (((fMax - R) / 6.0f) + (fDelta / 2.0f)) / fDelta;
            del_G = (((fMax - G) / 6.0f) + (fDelta / 2.0f)) / fDelta;
            del_B = (((fMax - B) / 6.0f) + (fDelta / 2.0f)) / fDelta;

            if (R == fMax) H = del_B - del_G;
            else if (G == fMax) H = (1.0f / 3.0f) + del_R - del_B;
            else if (B == fMax) H = (2.0f / 3.0f) + del_G - del_R;

            if (H < 0.0f) H += 1.0f;
            if (H > 1.0f)  H -= 1.0f;
        }

        *pHue = H;
        *pSat = S;
        *pLue = L;
    }

    /*
    * Convert color HSL to RGB
    * H HSL hue value                [0 - 1]
    * S HSL saturation value        [0 - 1]
    * L HSL luminance value            [0 - 1]
    */
    static DWORD HSLToRGB(float H, float S, float L)
    {
        BYTE R, G, B;
        float var_1, var_2;

        if (S == 0)                       //HSL from 0 to 1
        {
            R = G = B = (BYTE)(L * 255.0f);   //RGB results from 0 to 255
        }
        else
        {
            if (L < 0.5) var_2 = L * (1.0f + S);
            else           var_2 = (L + S) - (S * L);

            var_1 = 2.0f * L - var_2;

            R = (BYTE)(255.0f * HueToRGB(var_1, var_2, H + (1.0f / 3.0f)));
            G = (BYTE)(255.0f * HueToRGB(var_1, var_2, H));
            B = (BYTE)(255.0f * HueToRGB(var_1, var_2, H - (1.0f / 3.0f)));
        }

        return RGB(R, G, B);
    }

    /*
    * _HSLToRGB color HSL value to RGB
    * clr  RGB color value
    * nHue HSL hue value            [0 - 360]
    * nSat HSL saturation value        [0 - 200]
    * nLue HSL luminance value        [0 - 200]
    */
#define _HSLToRGB(h,s,l) (0xFF << 24 | HSLToRGB((float)h / 360.0f,(float)s / 200.0f,l / 200.0f))

    ///////////////////////////////////////////////////////////////////////
    //
    //
    IMPLEMENT_DUICONTROL(CColorPaletteUI)

    CColorPaletteUI::CColorPaletteUI()
        : m_uButtonState(0)
        , m_bIsInBar(false)
        , m_bIsInPallet(false)
        , m_nCurH(180)
        , m_nCurS(200)
        , m_nCurB(100)
        , m_nPalletHeight(200)
        , m_nBarHeight(10)
        , m_pBits(NULL)
    {
        memset(&m_bmInfo, 0, sizeof(m_bmInfo));

        m_hMemBitmap=NULL;
    }

    CColorPaletteUI::~CColorPaletteUI()
    {
        if (m_pBits) free(m_pBits);

        if (m_hMemBitmap) {
            ::DeleteObject(m_hMemBitmap);
        }

    }

    DWORD CColorPaletteUI::GetSelectColor()
    {
        DWORD dwColor = _HSLToRGB(m_nCurH, m_nCurS, m_nCurB);
        return 0xFF << 24 | GetRValue(dwColor) << 16 | GetGValue(dwColor) << 8 | GetBValue(dwColor);
    }

    void CColorPaletteUI::SetSelectColor(DWORD dwColor) 
    {
        float H = 0, S = 0, B = 0;
        COLORREF dwBkClr = RGB(GetBValue(dwColor),GetGValue(dwColor),GetRValue(dwColor));
        RGBToHSL(dwBkClr, &H, &S, &B);
        m_nCurH = (int)(H*360);
        m_nCurS = (int)(S*200);
        m_nCurB = (int)(B*200);
        UpdatePalletData();
        NeedUpdate();
    }

    LPCTSTR CColorPaletteUI::GetClass() const
    {
        return _T("ColorPaletteUI");
    }

    LPVOID CColorPaletteUI::GetInterface(LPCTSTR pstrName)
    {
        if (_tcscmp(pstrName, DUI_CTR_COLORPALETTE) == 0) return static_cast<CColorPaletteUI*>(this);
        return CControlUI::GetInterface(pstrName);
    }

    void CColorPaletteUI::SetPalletHeight(int nHeight)
    {
        m_nPalletHeight = nHeight;
    }
    int     CColorPaletteUI::GetPalletHeight() const
    {
        return m_nPalletHeight;
    }
    void CColorPaletteUI::SetBarHeight(int nHeight)
    {
        if (nHeight>150) {
            nHeight = 150; //限制最大高度，由于当前设计，nheight超出190，程序会因越界访问崩溃
        }
        m_nBarHeight = nHeight;
    }
    int  CColorPaletteUI::GetBarHeight() const
    {
        return m_nBarHeight;
    }

    void CColorPaletteUI::SetThumbImage(LPCTSTR pszImage)
    {
        if (m_strThumbImage != pszImage)
        {
            m_strThumbImage = pszImage;
            NeedUpdate();
        }
    }

    LPCTSTR CColorPaletteUI::GetThumbImage() const
    {
        return m_strThumbImage.GetData();
    }

    void CColorPaletteUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
    {
        if (_tcscmp(pstrName, _T("palletheight")) == 0) SetPalletHeight(_ttoi(pstrValue));
        else if (_tcscmp(pstrName, _T("barheight")) == 0) SetBarHeight(_ttoi(pstrValue));
        else if (_tcscmp(pstrName, _T("thumbimage")) == 0) SetThumbImage(pstrValue);
        else CControlUI::SetAttribute(pstrName, pstrValue);
    }

    void CColorPaletteUI::DoInit()
    {
        m_MemDc = CreateCompatibleDC(GetManager()->GetPaintDC());
        m_hMemBitmap = CreateCompatibleBitmap(GetManager()->GetPaintDC(), 400, 360);
        SelectObject(m_MemDc, m_hMemBitmap);

        ::GetObject(m_hMemBitmap, sizeof(m_bmInfo), &m_bmInfo);
        DWORD dwSize = m_bmInfo.bmHeight * m_bmInfo.bmWidthBytes;
        m_pBits = (BYTE *)malloc(dwSize);
        ::GetBitmapBits(m_hMemBitmap, dwSize, m_pBits);
    }

    void CColorPaletteUI::SetPos(RECT rc, bool bNeedInvalidate)
    {
        CControlUI::SetPos(rc, bNeedInvalidate);

        m_ptLastPalletMouse.x = m_nCurH * (m_rcItem.right - m_rcItem.left) / 360 + m_rcItem.left;
        m_ptLastPalletMouse.y = (200 - m_nCurB) * m_nPalletHeight / 200 + m_rcItem.top;

        UpdatePalletData();
        UpdateBarData();
    }

    void CColorPaletteUI::DoEvent(TEventUI& event)
    {
        CControlUI::DoEvent(event);

        if (event.Type == UIEVENT_BUTTONDOWN)
        {
            if (event.ptMouse.x >= m_rcItem.left && event.ptMouse.y >= m_rcItem.top &&
                event.ptMouse.x < m_rcItem.right && event.ptMouse.y < m_rcItem.top + m_nPalletHeight)
            {
                int x = (event.ptMouse.x - m_rcItem.left) * 360 / (m_rcItem.right - m_rcItem.left);
                int y = (event.ptMouse.y - m_rcItem.top) * 200 / m_nPalletHeight;
                x = min(max(x, 0), 360);
                y = min(max(y, 0), 200);

                m_ptLastPalletMouse = event.ptMouse;
                if (m_ptLastPalletMouse.x < m_rcItem.left) m_ptLastPalletMouse.x = m_rcItem.left;
                if (m_ptLastPalletMouse.x > m_rcItem.right) m_ptLastPalletMouse.x = m_rcItem.right;
                if (m_ptLastPalletMouse.y < m_rcItem.top) m_ptLastPalletMouse.y = m_rcItem.top;
                if (m_ptLastPalletMouse.y > m_rcItem.top + m_nPalletHeight) m_ptLastPalletMouse.y = m_rcItem.top + m_nPalletHeight;

                m_nCurH = x;
                m_nCurB = 200 - y;

                m_uButtonState |= UISTATE_PUSHED;
                m_bIsInPallet = true;
                m_bIsInBar = false;

                UpdateBarData();
            }

            if (event.ptMouse.x >= m_rcItem.left && event.ptMouse.y >= m_rcItem.bottom - m_nBarHeight &&
                event.ptMouse.x < m_rcItem.right && event.ptMouse.y < m_rcItem.bottom)
            {
                m_nCurS = (event.ptMouse.x - m_rcItem.left) * 200 / (m_rcItem.right - m_rcItem.left);
                m_uButtonState |= UISTATE_PUSHED;
                m_bIsInBar = true;
                m_bIsInPallet = false;
                UpdatePalletData();
            }

            Invalidate();
            return;
        }
        if (event.Type == UIEVENT_BUTTONUP)
        {
            DWORD color=0;
            if ((m_uButtonState | UISTATE_PUSHED) && (IsEnabled()))
            {
                color = GetSelectColor();
                m_pManager->SendNotify(this, DUI_MSGTYPE_COLORCHANGED, color, 0);
            }

            m_uButtonState &= ~UISTATE_PUSHED;
            m_bIsInPallet = false;
            m_bIsInBar = false;

            Invalidate();
            return;
        }
        if (event.Type == UIEVENT_MOUSEMOVE)
        {
            if (!(m_uButtonState &UISTATE_PUSHED))
            {
                m_bIsInBar = false;
                m_bIsInPallet = false;
            }
            if (m_bIsInPallet == true)
            {
                POINT pt = event.ptMouse;
                pt.x -= m_rcItem.left;
                pt.y -= m_rcItem.top;

                if (pt.x >= 0 && pt.y >= 0 && pt.x <= m_rcItem.right && pt.y <= m_rcItem.top + m_nPalletHeight)
                {
                    int x = pt.x * 360 / (m_rcItem.right - m_rcItem.left);
                    int y = pt.y * 200 / m_nPalletHeight;
                    x = min(max(x, 0), 360);
                    y = min(max(y, 0), 200);

                    m_ptLastPalletMouse = event.ptMouse;
                    if (m_ptLastPalletMouse.x < m_rcItem.left) m_ptLastPalletMouse.x = m_rcItem.left;
                    if (m_ptLastPalletMouse.x > m_rcItem.right) m_ptLastPalletMouse.x = m_rcItem.right;
                    if (m_ptLastPalletMouse.y < m_rcItem.top) m_ptLastPalletMouse.y = m_rcItem.top;
                    if (m_ptLastPalletMouse.y >= m_rcItem.top + m_nPalletHeight) m_ptLastPalletMouse.y = m_rcItem.top + m_nPalletHeight;

                    m_nCurH = x;
                    m_nCurB = 200 - y;

                    UpdateBarData();
                }
            }
            else if (m_bIsInBar == true)
            {
                m_nCurS = (event.ptMouse.x - m_rcItem.left) * 200 / (m_rcItem.right - m_rcItem.left);
                m_nCurS = min(max(m_nCurS, 0), 200);
                UpdatePalletData();
            }

            Invalidate();
            return;
        }

    }

    void CColorPaletteUI::PaintBkColor(HDC hDC)
    {
        PaintPallet(hDC);
    }

    void CColorPaletteUI::PaintPallet(HDC hDC)
    {
        int nSaveDC = ::SaveDC(hDC);

        ::SetStretchBltMode(hDC, HALFTONE);
        //拉伸模式将内存图画到控件上
        StretchBlt(hDC, m_rcItem.left, m_rcItem.top, m_rcItem.right - m_rcItem.left, m_nPalletHeight, m_MemDc, 0, 1, 360, 200, SRCCOPY);
        StretchBlt(hDC, m_rcItem.left, m_rcItem.bottom - m_nBarHeight, m_rcItem.right - m_rcItem.left, m_nBarHeight, m_MemDc, 0, 210, 200, m_nBarHeight, SRCCOPY);

        RECT rcCurSorPaint = { m_ptLastPalletMouse.x - 4, m_ptLastPalletMouse.y - 4, m_ptLastPalletMouse.x + 4, m_ptLastPalletMouse.y + 4 };
        CRenderEngine::DrawImageString(hDC, m_pManager, rcCurSorPaint, m_rcPaint, m_strThumbImage);

        rcCurSorPaint.left = m_rcItem.left + m_nCurS * (m_rcItem.right - m_rcItem.left) / 200 - 4;
        rcCurSorPaint.right = m_rcItem.left + m_nCurS * (m_rcItem.right - m_rcItem.left) / 200 + 4;
        rcCurSorPaint.top = m_rcItem.bottom - m_nBarHeight / 2 - 4;
        rcCurSorPaint.bottom = m_rcItem.bottom - m_nBarHeight / 2 + 4;
        CRenderEngine::DrawImageString(hDC, m_pManager, rcCurSorPaint, m_rcPaint, m_strThumbImage);
        ::RestoreDC(hDC, nSaveDC);
    }

    void CColorPaletteUI::UpdatePalletData()
    {
        int x, y;
        BYTE *pPiexl;
        DWORD dwColor;
        for (y = 0; y < 200; ++y) {
            for (x = 0; x < 360; ++x) {
                pPiexl = LPBYTE(m_pBits) + ((200 - y)*m_bmInfo.bmWidthBytes) + ((x*m_bmInfo.bmBitsPixel) / 8);
                dwColor = _HSLToRGB(x, m_nCurS, y);
                if(dwColor == 0xFF000000) dwColor = 0xFF000001;
                pPiexl[0] = GetBValue(dwColor);
                pPiexl[1] = GetGValue(dwColor);
                pPiexl[2] = GetRValue(dwColor);
            }
        }

        SetBitmapBits(m_hMemBitmap, m_bmInfo.bmWidthBytes * m_bmInfo.bmHeight, m_pBits);
    }


    void CColorPaletteUI::UpdateBarData()
    {
        int x, y;
        BYTE *pPiexl;
        DWORD dwColor;
        //这里画出Bar
        for (y = 0; y < m_nBarHeight; ++y) {
            for (x = 0; x < 200; ++x) {
                pPiexl = LPBYTE(m_pBits) + ((210 + y)*m_bmInfo.bmWidthBytes) + ((x*m_bmInfo.bmBitsPixel) / 8);
                dwColor = _HSLToRGB(m_nCurH, x, m_nCurB);
                if(dwColor == 0xFF000000) dwColor = 0xFF000001;
                pPiexl[0] = GetBValue(dwColor);
                pPiexl[1] = GetGValue(dwColor);
                pPiexl[2] = GetRValue(dwColor);
            }
        }

        SetBitmapBits(m_hMemBitmap, m_bmInfo.bmWidthBytes * m_bmInfo.bmHeight, m_pBits);
    }

}